# crude test builder based on
# - https://github.com/esp8266/Arduino/blob/3.0.2/tests/host/Makefile
# - https://github.com/mcspr/rpnlib/blob/0.24.1/examples/host/CMakeLists.txt
#
# we do require certain pre-requisites
# - https://github.com/esp8266/Arduino/ git tree for both Core's and Mock files
# - https://github.com/ThrowTheSwitch/Unity git tree (or tool-unity from platformio)
#
# after everything is installed
# $ cmake -B build
# $ cmake --build build --target test

cmake_minimum_required(VERSION 3.18)
project(host-test VERSION 1 LANGUAGES C CXX)

set(CMAKE_C_STANDARD 11 CACHE STRING "Global C standard version (...does not yet work with 17 though)")
set(CMAKE_CXX_STANDARD 17 "Global C++ standard version")

# required for esp8266 host mocking
set(COMMON_FLAGS
    -Os
    -g
    -fno-common
    -funsigned-char
    -DPROGMEM_STRING_ATTR=
    -DCORE_MOCK
    -DHOST_MOCK=1
    -DLWIP_IPV6=0
    -Dstrnlen_P=strnlen
    -Dmemcmp_P=memcmp
    -Dstrncasecmp_P=strncasecmp
    -DUNITY_INCLUDE_DOUBLE
)

set(ESPURNA_PATH ${CMAKE_SOURCE_DIR}/../../../ CACHE PATH "ESPurna source code repo root")

# Make sure to generate compile_commands.json
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# PIO does not really make it easy to install packages outside of the 'platform' context,
# so sharing these between a normal builder might not be an option. (...big TODO though)
# for right now, just fetch these as raw repos. plus, there's no need for any extra params

set(unity_version v2.6.0)

include(FetchContent)
FetchContent_Declare(
    unitygit
    GIT_REPOSITORY https://github.com/ThrowTheSwitch/Unity
    GIT_TAG ${unity_version}
    GIT_CONFIG core.autocrlf=false core.eol=lf
    SOURCE_DIR ${CMAKE_SOURCE_DIR}/cache/unitygit-${unity_version}-src
    SUBBUILD_DIR ${CMAKE_SOURCE_DIR}/cache/unitygit-${unity_version}-subbuild
)

FetchContent_MakeAvailable(unitygit)
FetchContent_GetProperties(unitygit SOURCE_DIR)

target_compile_options(unity BEFORE PRIVATE
    -DUNITY_OUTPUT_COLOR
    -DUNITY_INCLUDE_DOUBLE
)

set(arduinojson_version v5.13.5)

FetchContent_Declare(
    ArduinoJson
    GIT_REPOSITORY https://github.com/bblanchon/ArduinoJson
    GIT_TAG ${arduinojson_version}
    GIT_CONFIG core.autocrlf=false core.eol=lf
    SOURCE_DIR ${CMAKE_SOURCE_DIR}/cache/arduinojson-${arduinojson_version}-src
    SOURCE_SUBDIR src
    SUBBUILD_DIR ${CMAKE_SOURCE_DIR}/cache/arduinojson-${arduinojson_version}-subbuild
)

FetchContent_MakeAvailable(ArduinoJson)
FetchContent_GetProperties(ArduinoJson SOURCE_DIR)

set(esp8266_version ccea72823ac50290bc05c67350d2be6626e65547)

FetchContent_Declare(
    esp8266git
    GIT_REPOSITORY https://github.com/esp8266/Arduino/
    GIT_TAG ${esp8266_version}
    GIT_CONFIG core.autocrlf=false core.eol=lf
    GIT_SUBMODULES libraries/LittleFS/lib/littlefs libraries/ESP8266SdFat
    GIT_SUBMODULES_RECURSE FALSE
    SOURCE_DIR ${CMAKE_SOURCE_DIR}/cache/esp8266git-src
    SUBBUILD_DIR ${CMAKE_SOURCE_DIR}/cache/esp8266git-subbuild
)

FetchContent_MakeAvailable(esp8266git)
FetchContent_GetProperties(esp8266git SOURCE_DIR)

# mock'ed Arduino Core headers sometimes expect to be included with some pre-requisites, which we obviously don't have
add_library(common INTERFACE)
target_compile_options(common INTERFACE
    "SHELL:-include ${esp8266git_SOURCE_DIR}/tests/host/common/mock.h"
    "SHELL:-include ${esp8266git_SOURCE_DIR}/tests/host/common/c_types.h"
)

# try to hack esp8266 host test layer
# - we need to specify bunch of things that the original Makefile does
# - there are a lot of cross-dependencies, we need to include a lot of .cpp files here
add_library(esp8266 STATIC
    src/ArduinoMainOverride.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/Arduino.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/ArduinoMainUdp.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/WMath.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/MockUART.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/MockTools.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/MocklwIP.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/MockEsp.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/UdpContextSocket.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/user_interface.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/HostWiring.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/md5.c
    ${esp8266git_SOURCE_DIR}/tests/host/common/noniso.c
    ${esp8266git_SOURCE_DIR}/tests/host/common/flash_hal_mock.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/spiffs_mock.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/littlefs_mock.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/sdfs_mock.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/ArduinoMainUdp.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/ArduinoMainSpiffs.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/ArduinoMainLittlefs.cpp
    ${esp8266git_SOURCE_DIR}/tests/host/common/user_interface.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/debug.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/core_esp8266_noniso.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/stdlib_noniso.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/WString.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/HardwareSerial.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/Print.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/Schedule.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/time.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/Stream.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/StreamSend.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/FS.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs_api.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs/spiffs_cache.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs/spiffs_check.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs/spiffs_gc.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs/spiffs_hydrogen.cpp
    ${esp8266git_SOURCE_DIR}/cores/esp8266/spiffs/spiffs_nucleus.cpp
    ${esp8266git_SOURCE_DIR}/libraries/LittleFS/src/LittleFS.cpp
    ${esp8266git_SOURCE_DIR}/libraries/LittleFS/src/lfs.c
    ${esp8266git_SOURCE_DIR}/libraries/LittleFS/src/lfs_util.c
)
target_include_directories(esp8266 PUBLIC
    ${esp8266git_SOURCE_DIR}/tests/host/common/
    ${esp8266git_SOURCE_DIR}/tests/host
    ${esp8266git_SOURCE_DIR}/tools/sdk/lwip2/include
    ${esp8266git_SOURCE_DIR}/tools/sdk/include
    ${esp8266git_SOURCE_DIR}/cores/esp8266/
    ${esp8266git_SOURCE_DIR}/libraries/LittleFS/src/
    ${esp8266git_SOURCE_DIR}/libraries/SPI/
    ${esp8266git_SOURCE_DIR}/libraries/ESP8266SdFat/src
)
target_compile_options(esp8266 PUBLIC
    ${COMMON_FLAGS}
    -DF_CPU=80000000
    -Wl,--defsym,_FS_start=0x40300000
    -Wl,--defsym,_FS_end=0x411FA000
    -Wl,--defsym,_FS_page=0x100
    -Wl,--defsym,_FS_block=0x2000
    -Wl,--defsym,_EEPROM_start=0x411fb000
)
target_link_libraries(esp8266 PUBLIC common)

# our library source (maybe some day this will be a simple glob)
add_library(espurna STATIC
    ${ESPURNA_PATH}/code/espurna/fs_math.c
    ${ESPURNA_PATH}/code/espurna/settings_convert.cpp
    ${ESPURNA_PATH}/code/espurna/terminal_commands.cpp
    ${ESPURNA_PATH}/code/espurna/terminal_parsing.cpp
    ${ESPURNA_PATH}/code/espurna/datetime.cpp
    ${ESPURNA_PATH}/code/espurna/types.cpp
    ${ESPURNA_PATH}/code/espurna/utils.cpp
)
target_link_libraries(espurna PUBLIC esp8266)
target_link_options(espurna PUBLIC
    -fsanitize=address
)
target_include_directories(espurna PUBLIC
    ${ESPURNA_PATH}/code/
    ${CMAKE_SOURCE_DIR}/cache/arduinojson-${arduinojson_version}-src/src
)
target_compile_options(espurna PUBLIC
    ${COMMON_FLAGS}
    -fsanitize=address
)
target_compile_options(espurna PRIVATE
    -Wall
    -Wextra
)

# extra code linked w/ the unity for every test executable
add_library(unity-local STATIC
    ${CMAKE_SOURCE_DIR}/unity/unity_fixtures.c
    ${CMAKE_SOURCE_DIR}/unity/unity_extra.cpp
)
target_link_libraries(unity-local PUBLIC espurna unity)
target_include_directories(unity-local PUBLIC
    ${CMAKE_SOURCE_DIR}/unity
)
target_compile_options(unity-local PUBLIC
    ${COMMON_FLAGS}
)

# each case is built separately, we expect these to work like a normal executable
include(CTest)
list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")

function(build_tests)
    foreach(ARG IN LISTS ARGN)
        file(GLOB ${ARG}_sources "src/${ARG}/*.h" "src/${ARG}/*.cpp")
        add_executable(test-${ARG} ${${ARG}_sources})
        target_link_libraries(test-${ARG} espurna unity unity-local)
        target_compile_options(test-${ARG} PRIVATE
            ${COMMON_FLAGS}
            -Wall
            -Wextra
        )
        set_target_properties(test-${ARG} PROPERTIES COMPILE_FLAGS -g)
        add_test(NAME ${ARG} COMMAND test-${ARG})
    endforeach()
endfunction()

build_tests(
    basic
    embedis
    filters
    led
    mqtt
    scheduler
    sensor
    settings
    terminal
    tuya
    types
    url
    utils
)
